feat: add My Account API support for managing MFA authentication method#835
feat: add My Account API support for managing MFA authentication method#835utkrishtsahu wants to merge 6 commits into
Conversation
| private const val MY_ACCOUNT_DELETE_AUTH_METHOD_METHOD = | ||
| "myAccount#deleteAuthenticationMethod" | ||
|
|
||
| class DeleteAuthenticationMethodRequestHandler : |
There was a problem hiding this comment.
is final class required here?
There was a problem hiding this comment.
No, final class is not used here — it's a regular class implementing the MyAccountRequestHandler interface. Since it's implementing an interface, it can't be final. No change needed.
| override fun onSuccess( | ||
| res: AuthenticationMethod | ||
| ) { | ||
| result.success(null) |
There was a problem hiding this comment.
is flutter compatible with suspend functions?
There was a problem hiding this comment.
Flutter's MethodChannel on Android uses callback-based MethodChannel.Result, not coroutines. The Auth0 Android SDK's Request.start(Callback) pattern is the correct approach for MethodChannel handlers. Suspend functions would require a coroutine scope which adds unnecessary complexity here. This is the same pattern used by all other handlers in the plugin (WebAuth, Auth, CredentialsManager).
| result.success(res.map { | ||
| mapOf( | ||
| "name" to it.type, | ||
| "enabled" to true |
There was a problem hiding this comment.
Factor has type and usage. Dont we want to pass usage as well to dart?
There was a problem hiding this comment.
Agree, usage array should be returned
There was a problem hiding this comment.
Fixed. Updated to pass "type" and "usage" directly from the native Factor object instead of mapping to "name"/"enabled".
| result.success(res.map { | ||
| mapOf( | ||
| "name" to it.type, | ||
| "enabled" to true |
There was a problem hiding this comment.
"enabled" : true should this be derived from response.?
There was a problem hiding this comment.
Fixed. Removed enabled entirely — the API doesn't return an enabled property. Replaced with "usage" which is the actual field from the SDK (List? like ["secondary"]).
| override fun onSuccess(res: List<Factor>) { | ||
| result.success(res.map { | ||
| mapOf( | ||
| "name" to it.type, |
There was a problem hiding this comment.
Lets keep it as type only. Its not actually a name thats returned.
There was a problem hiding this comment.
Fixed. Changed from "name" to it.type to "type" to it.type.
| result.success(res.map { | ||
| mapOf( | ||
| "name" to it.type, | ||
| "enabled" to true |
There was a problem hiding this comment.
What exactly does this enabled represent. The API doesn't return an enabled property
There was a problem hiding this comment.
You're right — removed it. Replaced with the actual usage array from the SDK. Updated the Dart Factor model accordingly (type + usage instead of name + enabled).
| request: MethodCallRequest, | ||
| result: MethodChannel.Result | ||
| ) { | ||
| client.getAuthenticationMethods() |
There was a problem hiding this comment.
This API now supports a type parameter as input. Android PR is already up.
auth0/Auth0.Android#974
Both platforms will have this out by next week. Update accordingly
There was a problem hiding this comment.
Acknowledged. Will add the optional type filter parameter to getAuthenticationMethods() once Auth0.Android PR #974 is merged and the corresponding Swift SDK update is available. Will address in a follow-up commit/PR.
| override fun onSuccess( | ||
| res: AuthenticationMethod | ||
| ) { | ||
| result.success(null) |
There was a problem hiding this comment.
Why is the success returning null here. Shouldn't the details from AuthenticationMethod type be returned here ?
There was a problem hiding this comment.
Fixed. Now returning res.toMyAccountMethodMap() on success. Updated the Dart return type from Future to Future across the platform interface and public API. iOS handler also updated to return method.asDictionary().
| val handler = myAccountRequestHandlers.find { it.method == call.method } | ||
| if (handler != null) { | ||
| val accessToken = request.data["accessToken"] as? String ?: "" | ||
| val client = MyAccountAPIClient(request.account, accessToken) |
There was a problem hiding this comment.
MyAccountAPI's support DPoP too. Ensure the client created can support DPoP also.
auth0/Auth0.Android#974
This PR adds the support for the same in Android
There was a problem hiding this comment.
Acknowledged. Will add DPoP support to the MyAccountAPIClient creation once Auth0.Android PR #974 is merged and the Swift SDK adds equivalent support. This will involve passing a useDPoP flag from Dart and configuring the client accordingly. Will address in a follow-up once both native SDKs have this merged.
| put("_statusCode", exception.statusCode) | ||
| put("_errorFlags", mapOf( | ||
| "isNetworkError" to exception.isNetworkError, | ||
| )) |
There was a problem hiding this comment.
The MyAccountException returns more info which actually represents what went wrong like title, detail etc. These help the customer know what went wrong and also help us debug when an issue arises. Currently the above map captures only the status code, which in itself is not helpful. Add the other properties too
There was a problem hiding this comment.
Fixed. Added title and detail to the error map on both Android and iOS. Also exposed them as first-class properties on the Dart MyAccountException class (exception.title, exception.detail).
| return buildMap { | ||
| put("id", id) | ||
| put("type", type) | ||
| put("created_at", createdAt) |
There was a problem hiding this comment.
there is a usage property also
There was a problem hiding this comment.
Fixed. Added usage to the AuthenticationMethod serialization (inside the MfaAuthenticationMethod check alongside confirmed). Also added it to the Dart model.
| is PushNotificationAuthenticationMethod -> { | ||
| put("name", method.name) | ||
| } | ||
| else -> {} |
There was a problem hiding this comment.
Passkey , Recovery code and Password are missing
There was a problem hiding this comment.
Added an else branch that sets name to null for unhandled subtypes (Passkey, RecoveryCode, Password). These types don't have additional properties beyond the base id, type, createdAt that are already serialized for all AuthenticationMethod instances. Full passkey enrollment support is deferred to a future release due to WebAuthn/FIDO2 platform complexity.
| put("name", method.name) | ||
| } | ||
| is PushNotificationAuthenticationMethod -> { | ||
| put("name", method.name) |
There was a problem hiding this comment.
The confirmed property is also missing which is important to know whether a authentication method is verfied or nor
There was a problem hiding this comment.
Fixed. confirmed was already being serialized on Android (inside the MfaAuthenticationMethod check at line 39). Also added it to the iOS AuthenticationMethod.asDictionary() and the Dart AuthenticationMethod model so it's accessible as method.confirmed.
| put("recovery_code", challenge.recoveryCode) | ||
| } | ||
| is MfaEnrollmentChallenge -> {} | ||
| else -> {} |
There was a problem hiding this comment.
Oob enrollment challenge is missing
There was a problem hiding this comment.
Acknowledged. Will add OOB (Out-of-Band) enrollment challenge support in a follow-up once the full OOB enrollment flow is confirmed across both native SDKs.
… mapping Signed-off-by: utkrishtS <utkrisht.sahu@okta.com>
… models and verifyOtp return type
📋 Changes
Adds support for the Auth0 My Account API, enabling end-users to self-manage their MFA authentication methods without requiring Management API tokens.
New public API:
New types added:
Platform implementation:
Requires access tokens with audience https://{domain}/me/ and appropriate me scopes (read:me:authentication_methods, create:me:authentication_methods, delete:me:authentication_methods, read:me:factors).
📎 References
SDK-8730
🎯 Testing
Unit tests:
Manual testing:
Not tested: